
#include "Misc.h"
#include "Platform.h"
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <regex>
#include "myLog.h"

// #pragma GCC diagnostic ignored "-Wcomment"

using namespace std;

const char g_logoText[] = {
    R"""(
                 Etonetech Radar AD Component



                           _ooOoo_
                          o8888888o
                          88" . "88
                          (| -_- |)
                          O\  =  /O
                       ____/`---'\____
                     .'  \\|     |//  `.
                    /  \\|||  :  |||//  \
                   /  _||||| -:- |||||-  \
                   |   | \\\  -  /// |   |
                   | \_|  ''\---/''  |   |
                   \  .-\__  `-`  ___/-. /
                 ___`. .'  /--.--\  `. . __
              ."" '<  `.___\_<|>_/___.'  >'"".
             | | :  `- \`.;`\ _ /`;.`/ - ` : | |
             \  \ `-.   \_ __\ /__ _/   .-` /  /
        ======`-.____`-.___\_____/___.-`____.-'======
                           `=---='
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
               // 佛祖保佑 永不宕机 永无BUG //



    )"""
};

void showLogo() 
{
    cerr << g_logoText << endl;
}

int majorVersion() 
{
    return MAJORVER;
}

int minorVersion() 
{
    return MINORVER;
}

int patchVersion() 
{
    return PATCHVER;
}

void showVersion() 
{
#ifdef ETWS_DEBUG 
    auto isDebug = true;
#else
    auto isDebug = false;
#endif

    auto ver = to_string(majorVersion()) + ".";
    ver += to_string(minorVersion()) + ".";
    ver += to_string(patchVersion());
    auto compileVer = true == isDebug ? "debug" : "release";

    cout << "application version: " << ver << "  " << compileVer << endl;

    if (true == isDebug) 
    {
        cout << "defined 'ETWS_DEBUG'" << endl;
    }
    else 
    {
        cout << "not defined 'ETWS_DEBUG'" << endl;
    }
}

const std::string getErrorInfo(const int errNum)
{
    char buf[1024] = { 0 };
    // cache info use external buffer,ensure it is thread-safe.
    // return pointer never be nullptr.
    auto beginInBuf = strerror_r(errNum,buf,sizeof(buf));
    buf[sizeof(buf) - 1] = '\0';
    std::string error(beginInBuf);
    return error;
}

void capitalize(std::string &words)
{
    auto size = words.size();
    for(size_t i = 0; i < size; ++i)
    {
        if(words[0] >= 'a' && words[0] <= 'z')
        {
            words[0] -= 32;
        }
        if(i >= 1 && ' ' == words[i - 1] && words[i] >= 'a' && words[i] <= 'z')
        {
            words[i] -= 32;
        }
    }

    size_t pos = 0;
    for(size_t i = 0; i < size; ++i)
    {
        if(' ' != words[i])
        {
            pos = i;
            break;
        }
    }

    for(size_t i = pos; i < size; ++i)
    {
        if(' ' == words[i] &&  i < size - 1 &&' ' == words[i + 1])
        {
            continue;
        }
    }
}

void trim(std::string &words,const char ch)
{
    size_t pos = 0;
    if(false == words.empty())
    {
        while(std::string::npos != (pos = words.find(ch,pos)))
        {
            words.erase(pos,1);
        }
    }
}

std::vector<std::string> split(const std::string &words, const std::string &key)
{
    std::vector<std::string> result;
    if(true == words.empty())
    {
        return result;
    }

    std::string::size_type pos;
    auto src = words;
    src += key;
    size_t size = src.size();

    std::string s;
    for (size_t i = 0; i < size; i++)
    {
        pos = src.find(key, i);
        if (pos < size)
        {
            s = src.substr(i, pos - i);
            result.push_back(s);
            i = pos + key.size() - 1;
        }
    }

    return result;
}

const std::string shell(const std::string &cmd,std::string &result)
{
    auto fp = popen(cmd.c_str(),"r");
    if(nullptr == fp)
    {
        return std::string("open pipe failed.");
    }

    char buffer[4096] = { 0 };
    auto blk = fread(buffer,sizeof(buffer),1,fp);
    if(0 != ferror(fp))
    {
        pclose(fp);
        return std::string("read shell result failed,read blk: ") + std::to_string(blk);
    }

    // close
    pclose(fp);
    
    // fill shell result.
    result = buffer;

    return std::string("");
}

const std::string shell(const std::string &cmd)
{
    if(true == cmd.empty()) 
    {
        return "command is empty";
    }
    auto ret = system(cmd.c_str());
    bool isNormalTerminate = WIFEXITED(ret);
    auto shellRet = WEXITSTATUS(ret);
    if(-1 == ret) 
    {
        int err = errno;
        auto detail = getErrorInfo(err);
        return "child process could not be created, or its status could not be retrieved. " + detail;
    }
    else if (false == isNormalTerminate) 
    {
        return "shell could not be executed in the child process, exit code: " + to_string(shellRet);
    }
    else if (0 != shellRet) 
    {
        return "the child shell exit code is not zero(treat this exit code as an error), exit code: " + to_string(shellRet);
    }

    return (cmd + string("Success executed."));
}

string executionPath()
{
#if defined(__linux)
    static char elfPath[4096];
    auto size = readlink("/proc/self/exe",elfPath,sizeof(elfPath));
    if(size <= 0 || size > (decltype(size))(sizeof(elfPath) - 1))
    {
        fprintf(stderr, "get execution path failed. %s %d\n",__FILE__,__LINE__);
        return "";
    }
    for(int i = size - 1;i >= 0;--i)
    {
        if('/' == elfPath[i])
        {
            elfPath[1 + i] = 0;
            return string(elfPath);
        }
        if(0 == i)
        {
            fprintf(stderr, "get execution path failed. %s %d\n",__FILE__,__LINE__);
            return "";
        }
    }
#endif
    return "";
}

string ipV4Addr(const string &ifaceName) 
{
    auto sock = ::socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0) 
    {
        return "";
    }

    ifreq ifr = { 0 };
    // iface name max bytes limit: 15 bytes.
    snprintf(ifr.ifr_ifrn.ifrn_name, IFNAMSIZ - 1, ifaceName.c_str());
    if (ioctl(sock, SIOCGIFADDR, &ifr) < 0) 
    {
        ::close(sock);
        sock = -1;
        return "";
    }

    auto sockAddr = reinterpret_cast<sockaddr_in *>(&ifr.ifr_addr);
    char ipStr[INET_ADDRSTRLEN] = { 0 };
    if (nullptr == inet_ntop(AF_INET, &sockAddr->sin_addr, ipStr, sizeof(ipStr) - 1)) 
    {
        int err = errno;
        auto info = getErrorInfo(err);
        fprintf(stderr, "%s\n", info.c_str());

        ::close(sock);
        sock = -1;
        return "";
    }

    return string(ipStr);
}

bool isIpV4Addr(const string &ipStr) 
{
    static const regex regExpr(R"(^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$)");

    if (false == regex_match(ipStr, regExpr)) 
    {
        return false;
    }

    return true;
}
